﻿#region License
/*
 * Microsoft Public License (Ms-PL)
 * 
 * This license governs use of the accompanying software. If you use the software, you accept this license. 
 * If you do not accept the license, do not use the software.
 *
 * 1. Definitions
 *
 * The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
 *
 * A "contribution" is the original software, or any additions or changes to the software.
 *
 * A "contributor" is any person that distributes its contribution under this license.
 *
 * "Licensed patents" are a contributor's patent claims that read directly on its contribution.
 *
 * 2. Grant of Rights
 *
 * (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
 * each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, 
 * prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
 *
 * (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
 * each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, 
 * use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the 
 * contribution in the software.
 *
 * 3. Conditions and Limitations
 *
 * (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
 *
 * (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, 
 * your patent license from such contributor to the software ends automatically.
 *
 * (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, 
 * and attribution notices that are present in the software.
 *
 * (D) If you distribute any portion of the software in source code form, you may do so only under this license by including 
 * a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, 
 * you may only do so under a license that complies with this license.
 *
 * (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. 
 * You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, 
 * the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
 */
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Xml;
using System.Net;
using ROL.Core.Utils;
using System.Collections;
using System.IO;

namespace ROLRouter.Base
{


    public class Server
    {
        public static ManualResetEvent clientConnected =
               new ManualResetEvent(false);
        public volatile bool _shouldStop;
        public static ArrayList ClientSockets;
        public static bool ContinueReclaim = true;
        // public static Thread ThreadReclaim;
        public static Thread cmds;
        public static Thread listen;
        public static Utilities Util;
        public TcpListener listener;




        public Server()
        {
        }

        public void Stop()
        {
            try
            {
                _shouldStop = true;
                ContinueReclaim = false;
                //clientConnected.Set();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        public void Start()
        {
            ThreadPool.SetMaxThreads(25, 25);
            Util = new Utilities();
            ClientSockets = new ArrayList();
            ThreadPool.QueueUserWorkItem(new WaitCallback(Reclaim));
            Thread.Sleep(2);
            string Ip = Util.GetRouterIPFromConfig();
            int Port = 7002;
            if (clientConnected.WaitOne(0))
                return;
            listener = new TcpListener(IPAddress.Parse(Ip), Port);
            try
            {
                listener.Start();

                int ClientNbr = 0;

                // Start listening for connections.
                Console.WriteLine("\nRouter waiting for connections on " + listener.LocalEndpoint.ToString());
                if (clientConnected.WaitOne(0))
                    return;
                while (true)
                {
                    TcpClient handler = null;
                    /* This throws a blocking exception when we try to stop this server
                     * so to get around this, we just catch the error here, and allow
                     * the server to stop immediatly
                     */
                    try
                    {
                        handler = listener.AcceptTcpClient();
                    }
                    catch { }
                    if (handler != null)
                    {
                        Console.WriteLine("\nClient#{0} accepted!", ++ClientNbr);
                        // An incoming connection needs to be processed.
                        lock (ClientSockets.SyncRoot)
                        {
                            int i = ClientSockets.Add(new ClientHandler(handler));
                            ((ClientHandler)ClientSockets[i]).Start();
                        }
                    }
                    break;
                }
                

                //listener.Stop();

                ContinueReclaim = false;
                //ThreadReclaim.Join();

                //foreach (Object Client in ClientSockets)
                //{
                //  ((ClientHandler)Client).Stop();
                // }

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            //Console.WriteLine("\nHit enter to continue...");
            //Console.Read();

        }


        private void Reclaim(object state)
        {

            while (ContinueReclaim)
            {
                lock (ClientSockets.SyncRoot)
                {
                    for (int x = ClientSockets.Count - 1; x >= 0; x--)
                    {
                        Object Client = ClientSockets[x];
                        // if (!((ClientHandler)Client).Alive)
                        //  {
                        //   ClientSockets.Remove(Client);
                        //   Console.WriteLine("A client left");
                        // }
                    }
                }
                Thread.Sleep(200);
            }
        }


        class ClientHandler
        {

            TcpClient ClientSocket;
            bool ContinueProcess = false;
            //Thread ClientThread;

            public ClientHandler(TcpClient ClientSocket)
            {
                this.ClientSocket = ClientSocket;
            }

            public void Start()
            {
                ContinueProcess = true;
                ThreadPool.QueueUserWorkItem(new WaitCallback(Process));
                //ClientThread = new Thread(new ThreadStart(Process));
                //ClientThread.Start();
            }

            private void Process(object state)
            {
                Utilities Util = new Utilities();
                // Incoming data from the client.
                string data = null;

                // Data buffer for incoming data.
                byte[] bytes;

                if (ClientSocket != null)
                {
                    NetworkStream networkStream = ClientSocket.GetStream();
                    ClientSocket.ReceiveTimeout = 100; // 1000 miliseconds

                    byte[] clientMsg = {0x14, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x75, 0x70, 0x64,
                                   0x61, 0x74, 0x65, 0x73, 0x20, 0x33, 0x20, 0x35, 0x32, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00};

                    while (ContinueProcess)
                    {
                        bytes = new byte[ClientSocket.ReceiveBufferSize];
                        try
                        {
                            int BytesRead = networkStream.Read(bytes, 0, (int)ClientSocket.ReceiveBufferSize);
                            if (BytesRead > 0)
                            {
                                // Handle packet here
                                data = Encoding.ASCII.GetString(bytes, 0, BytesRead);
                                string match = Encoding.ASCII.GetString(clientMsg, 0, clientMsg.Length);
                                // Show the data on the console.
                                Console.WriteLine("\nPacket received : {0}", data);

                                // What to reply as the patch server on port 6005, only have to do this once
                                // and then client goes back to ServerRouter
                                byte[] sendBytes = Util.RouteToPatchServer();


                                //Only packet we care about, otherwise give them patch info, ignore all else
                                if (data == match) // we wants patch server info
                                {
                                    // Default packet gives patch server info, so dont do anything here

                                }
                                else // We want to send the serverlist
                                {
                                    sendBytes = Util.GameServerListReply();
                                }

                                //Util.RouteToPatchServer();//Encoding.ASCII.GetBytes(data);
                                networkStream.Write(sendBytes, 0, sendBytes.Length);
                                //break;
                                //if (data == "quit") break;

                            }
                        }
                        catch (IOException) { } // Timeout
                        catch (SocketException)
                        {
                            Console.WriteLine("\nConection is broken!");
                            break;
                        }
                        Thread.Sleep(200);
                    } // while ( ContinueProcess )
                    networkStream.Close();
                    ClientSocket.Close();
                }
            }  // Process

        }
    }
}